FullCompiler.php

<?php

namespace Phad\Test\Compilation;


/**
 * Tests to verify compiler output
 *
 * Tests I may need to add in the future:
 * - multiple <p-data> nodes
 * - multiple <on> nodes
 * - <on> nodes nested inside <p-data> nodes
 * - item nodes nested inside other item nodes
 * - item nodes adjacent to other item nodes
 * - multiple routes
 * - general flow of logic (probably won't ever go in THIS one though)
 * 
 */
class FullCompiler extends \Phad\Tester {

    /** the template file contents */
    protected $template;

    public function prepare(){
        $this->template = file_get_contents($this->file('code/template/main.php'));
    }

    public function testBackendInputNodesRemoved(){
        $this->test('with indent');

        $view = <<<HTML
            <form item="Blog">
                <onsubmit><?php
                    \$BlogRow['slug'] = str_replace(' ', '-', strtolower(\$BlogRow['title']));
                    /** set phad_block to null to skip submission */
                    if (false)\$BlogItem->phad_mode = null;
                ?></onsubmit>
                <input type="text" name="title" maxlength="75">
                <textarea name="body" maxlength="2000" minlength="50"></textarea>

                <input type="backend" name="slug" minlength=4 maxlength=150 />
            </form>
        HTML;


        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);
        
        echo $output;

        $this->str_not_contains($output, 'type="backend"');
        
    }

    public function testRouteNodeRemoved(){
        $this->test('with indent');
        //this one passes, bc there is whitespace, therefore a textnode before the <route> node
        //so i run this test again, without the indent
        $view = <<<VIEW
            <route pattern="/route/no-items/"></route>
            <route pattern="/route/still-no-items/"></route>
            <div>
            <h1>No Items</h1>
            </div>
        VIEW;


        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);
        
        echo $output;

        $this->str_not_contains($output,'<route','</route>');
        

        $this->test("no indent");

        $view = <<<VIEW
        <route pattern="/route/no-items/"></route>
        <route pattern="/route/still-no-items/"></route>
        <div>
        <h1>No Items</h1>
        </div>
        VIEW;


        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);
        
        echo $output;

        $this->str_not_contains($output,'<route','</route>');
    }

    public function testIncludesOnSubmitWhenThereIsANestedItem(){
        
        $verify = '/*verify onsubmit presence*/';
        $form = <<<FORM
            <form item="Blog" target="/blog/{title}/">
                <onsubmit><?php $verify ?></onsubmit>
                <input type="text" name="title" maxlength="75">
                <div item="Candy"><?=\$Candy->id</div>
            </form>
        FORM;

        $compiler = new \Phad\TemplateCompiler($form);
        $output = $compiler->compile($form,$this->template);

        echo $output;

        $this->str_contains($output, $verify);
        $this->str_not_contains($output, '<onsubmit><?php');
    }

    public function testCanReadNode(){
        $code = <<<HTML
            <a href="/whatever/" access="role:guest">Am Link</a>
        HTML;
        $output = <<<HTML
            <?php if (\$phad->can_read_node(array (
                'href' => '/whatever/',
                'access' => 'role:guest',
                'tagName' => 'a',
            ))): ?>
                <a href="/whatever/">Am Link</a>
            <?php endif; ?>
        HTML;

        $compiler = new \Phad\TemplateCompiler($code);
        $compiled = $compiler->compile($code,$this->template);

        echo $compiled;

        $this->str_contains($compiled,array_map('trim', explode("\n", $output)));
        // $this->str_
    }

    public function testAddFormNodeInfoWithFileInput(){
        $form = <<<FORM
            <form item="Blog" target="/blog/{title}/">
                <input type="text" name="title" maxlength="75">
                <textarea name="body" maxlength="2000" minlength="50"></textarea>
                <input type="file" name="document" />
            </form>
        FORM;
        $expect = '<form enctype="multipart/form-data" action="" method="POST">';

        $compiler = new \Phad\TemplateCompiler($form);
        $output = $compiler->compile($form,$this->template);

        echo $output;

        // $this-
        $this->str_contains($output, $expect, '<input type="file" name="document">');
    }

    /**
     * @test setting action on a form
     * @test setting method on a form
     * @test removing `candelete` attribute from form nodes
     */
    public function testAddFormNodeInfo(){
        $form = <<<FORM
            <form item="Blog" target="/blog/{title}/" candelete="whatever">
                <input type="text" name="title" maxlength="75">
                <textarea name="body" maxlength="2000" minlength="50"></textarea>
            </form>
        FORM;
        $expect = '<form action="" method="POST">';

        $compiler = new \Phad\TemplateCompiler($form);
        $output = $compiler->compile($form,$this->template);

        echo $output;

        // $this-
        $this->str_contains($output, $expect);
    }

    /**
     * @test that code in <on s=404> is present in output
     */
    public function testOnS404(){

        //@note dec-6-2022 test failing bc $expect needs to be changed

        $view = <<<VIEW
            <div item="Blog">
                <on s=404><p>Type: <span prop="type"></span></p></on>
                <h1 prop="title"></h1>
                <p prop="description"></p>
            </div>
        VIEW;

        $expect = <<<'EXPECT'
                if ($_node['response_code'] == 404){
                    if ($_index === 0):
            ?><p>Type: <span><?=$Blog->type?></span></p><?php
        endif;


                }

        EXPECT;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);

        // echo $output;

        // $this-
        $this->str_contains_lines($output, $expect);

    }

    /**
     * @test that code in <on s=200> is present in output
     * @test that inside on <on>, a prop node's inner html (in the compiled output) will print $Item->prop_name
     */
    public function testOnS200(){
        $view = <<<VIEW
            <div item="Blog">
                <on s=200><p>Type: <span prop="type"></span></p></on>
                <h1 prop="title"></h1>
                <p prop="description"></p>
            </div>
        VIEW;

        $expect = <<<EXPECT
            if (\$BlogInfo->data_index === 0 ):
            ?><p>Type: <span><?=\$Blog->type?></span></p><?php
        endif;
        EXPECT;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);

        echo $output;

        $this->str_contains($output, $expect);
    }

    /**
     *
     * @test that an `<p-data>` node is printed to `$BlogItem->accessList[] = (array of access node info)` in compiled output
     */
    public function testAccess(){
        $view = <<<VIEW
            <div item="Blog">
                <p-data access="role:guest" handler="tlf:check_throttles" name="type" where="Blog.type LIKE :type" limit="0,2"></p-data>
                <h1 prop="title"></h1>
                <p prop="description"></p>
            </div>
        VIEW;

        $expect = <<<EXPECT
            'data_nodes' => array (
          0 => 
          array (
            'access' => 'role:guest',
            'handler' => 'tlf:check_throttles',
            'name' => 'type',
            'where' => 'Blog.type LIKE :type',
            'limit' => '0,2',
            'type' => 'node',
          ),
          1 => 
          array (
            'type' => 'default',
          ),
        ),
        EXPECT;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);

        echo $output;

        $this->str_contains($output, $expect);
    }

    /**
     * @test that <form>'s <onsubmit> node contents are included in the compiled output
     */
    public function testFormOnSubmit(){
        $view = <<<VIEW
            <form item="Baby">
                <onsubmit><?php \$Baby->title = "cats";?> ...dogs...</onsubmit>
                <h1 class="title" prop="title">Am Title</h1>
            </form>
        VIEW;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);
        // echo $output;
        // exit;
        $this->str_contains($output,
            [
            '<?php $Baby->title = "cats";?> ...dogs...',
            'foreach ($BabyInfo->rows as $RowIndex_Baby => $BabyRow ):',
            ]
        );
    }

    /**
     * @test that form property info is extracted from prop-nodes & included in compiled output
     */
    public function testFormProperties(){
        $view = <<<VIEW
            <form item="Blog" target="/blog/{title}/">
                <input type="text" name="title" maxlength="75">
                <textarea name="body" maxlength="2000" minlength="50"></textarea>
            </form>
        VIEW;
        $properties = 
        [
            'title' => 
            [
                'type' => 'text',
                'maxlength' => '75',
                'tagName' => 'input',
            ],
            'body' => 
            [
                'maxlength' => '2000',
                'minlength' => '50',
                'tagName' => 'textarea',
            ],
            'id' => 
            [
                'tagName' => 'input',
                'type' => 'hidden',
            ],
        ];

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);

        // echo $output;

        // remove all space-characters from both strings (compiled properties output is indented)
        $output = str_replace(' ', '', $output);
        $properties = var_export($properties,true);
        $properties = str_replace(' ', '', $properties);

        $this->str_contains($output, $properties);

    }

    /**
     * @test that target in `<form target="TARGET">` is included in the compiled output
     */
    public function testFormTarget(){
        $view = <<<VIEW
            <form item="Baby" target="/baby/{name}/">
                <onsubmit><?php \$Baby->title = "cats";?> ...dogs...</onsubmit>
                <h1 class="title" prop="title">Am Title</h1>
            </form>
        VIEW;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);
        // echo $output;
        // exit;
        $this->str_contains($output,
            [
            "'target' => '/baby/{name}/'",

            '<?php $Baby->title = "cats";?> ...dogs...',
            'foreach ($BabyInfo->rows as $RowIndex_Baby => $BabyRow ):',

            ]
        );
    }

    /**
     * @test that <x-item> nodes are removed AND the foreach item loop is still created
     */
    public function testXItem(){
        $view = <<<VIEW
            <h1>K</h1>
            <x-item class="class_gets_removed" item="Baby">
                <h1 class="title" prop="title">Am Title</h1>
                <p prop="description">Am some description</p>
            </x-item>
        VIEW;

        $compiler = new \Phad\TemplateCompiler($view);
        $output = $compiler->compile($view,$this->template);
        // echo $output;
        $this->str_contains($output,
            [
            'foreach ($BabyInfo->rows as $RowIndex_Baby => $BabyRow ):',
            '<h1 class="title"><?=$Baby->title?></h1>',
            'endforeach;',
            '<h1>K</h1>'
            ]
        );

        $this->str_not_contains($output,
            ['class_gets_removed',
            'x-item',
            ]
        );
    }

    /**
     * @test that sitemap information is present in the compiled output
     */
    public function testSitemap(){
        $view = <<<VIEW
            <route pattern="/blog/{slug}/"> 
                <sitemap 
                    sql="SELECT slug FROM blog" 
                    handler="testns:blogSitemapHandler"
                    priority=0.8 
                    last_mod=1354
                    changefreq=daily
                >
                </sitemap>
            </route>
        VIEW;

        $compiler = new \Phad\TemplateCompiler($view);

        $output = $compiler->compile($view,$this->template);

        $output = $output;

        echo "\n\n\n$output\n\n\n";

        $this->str_contains($output,
            [
                'Route'=>
                var_export([0=>['pattern'=>'/blog/{slug}/']],true),
                'Sitemap'=>
                var_export(
                    ['/blog/{slug}/'=>
                        [
                        'sql'=> 'SELECT slug FROM blog',
                        'handler'=> 'testns:blogSitemapHandler',
                        'priority'=>'0.8',
                        'last_mod'=>'1354',
                        'changefreq'=>'daily',
                        'pattern'=>'/blog/{slug}/'
                        ]
                    ]
                    ,true
                ),
            ]
        
        );
    }

    /**
     * @bug $output['routes'] is empty, but the routes DO get added to the compiled output 
     * @test that route information is present in compiled file
     */
    public function testSimpleRoute(){
        $view = <<<VIEW

            <route pattern="/some-page/"></route>
            <div class="Parent">
                <h1 class="title">Am Title</h1>
                <p>Am some description</p>
            </div>
        VIEW;

        $compiler = new \Phad\TemplateCompiler($view);

        $output = $compiler->compile($view,$this->template);
        echo $output;

        $this->str_contains($output,
            [
                var_export([0=>['pattern'=>'/some-page/']],true),
            ]
        
        );
    }

    /**
     * @test that compiler creates a foreach(): loop for items & property printers in property nodes
     * @todo create 'empty-object' array from prop values listed in the html, like 'Am Title' below, so the view can be displayed with the default/placeholder content
     */
    public function testItemCanReadRow(){
        //can_read_row() was removed from compiled view output and is now being called during read_data(), making this test obsolete, as it only checks for compiled output & does not test can_read_row() functionality itself.
        $this->disable();
        return;
        $view = <<<VIEW
            <div class="Parent" item="Baby" can_read_row>
                <h1 class="title" prop="title">Am Title</h1>
                <p prop="description">Am some description</p>
            </div>
        VIEW;

        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);

        echo $output;
        $this->str_contains($output,
            [
            // 'if (!$phad->can_read_row($BabyRow, $BabyInfo, $BabyInfo->name)){',
            ]
        );

        $this->str_not_contains($output, 'can_read_row>');
    }

    /**
     * @test that compiler creates a foreach(): loop for items & property printers in property nodes
     * @todo create 'empty-object' array from prop values listed in the html, like 'Am Title' below, so the view can be displayed with the default/placeholder content
     */
    public function testItem(){
        $view = <<<VIEW
            <div class="Parent" item="Baby">
                <h1 class="title" prop="title">Am Title</h1>
                <p prop="description">Am some description</p>
            </div>
        VIEW;

        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);

        echo $output;
        $this->str_contains($output,
            [
            'foreach ($BabyInfo->rows as $RowIndex_Baby => $BabyRow ):',
            '<h1 class="title"><?=$Baby->title?></h1>',
            'endforeach;',
            ]
        
        );
        $this->str_not_contains($output,
            [
            'if (!$phad->can_read_row($BabyRow, $BabyInfo, $Baby)){',
            ]
        
        );
    }

    /**
     * @test that compiler has any output
     */
    public function testOutput(){
        $view = <<<VIEW
            <div class="Parent">
                <h1 class="title">Am Title</h1>
                <p>Am some description</p>
            </div>
        VIEW;


        $compiler = new \Phad\TemplateCompiler();
        $output = $compiler->compile($view, $this->template);
        
        echo $output;

        $this->str_contains($output, 'class="Parent"');
        $this->str_contains($output, 'class="title"');
        $this->str_contains($output,'<p>Am');
        
    }
}